Skip to content

refactor: wrap comic page-render + refine-render as creative tools (route-level persistence) (#2241)#2244

Merged
atomantic merged 1 commit into
mainfrom
claim/issue-2241
Jul 7, 2026
Merged

refactor: wrap comic page-render + refine-render as creative tools (route-level persistence) (#2241)#2244
atomantic merged 1 commit into
mainfrom
claim/issue-2241

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Closes #2241

Summary

Completes the #2220 audit clause the covers slice (#2234) deferred: gives the comic page render and refine-render route handlers the same treatment #2234 gave covers. The enqueue+persist flow moves out of the route handlers into shared service entry points, then those are wrapped as Creative Director tools so the routes and the CDO orchestrator share ONE code path.

Mirrors the renderComicCover / makeCoverRenderHandler structure exactly.

Service (server/services/pipeline/visualStages.js)

  • New persistComicPageSlot(issueId, pageIndex, {...}) — shared helper that splices the in-flight render slot onto stages.comicPages.pages[pageIndex].{proofImage|finalImage} through the serialized updateStageWithLatest issue write tail (404s on an out-of-range index).
  • New renderComicPage(issueId, options) — enqueue (enqueueVisualComicPage) + persist in one call.
  • refineComicPageRender now persists its slot too and returns { issue, stage }.

Routes (server/routes/pipeline/issues.js)

  • POST .../pages/:pageIndex/render and .../refine-render now delegate to the shared entry points (route keeps its up-front page-existence pre-flight + Zod validation + response shaping). Removed the now-duplicated inline enqueue+persist and the now-unused enqueueVisualComicPage / buildRenderSlot / slotKeyForVariant imports.

Creative tools (server/services/creative/tools/pipeline.js)

  • New pipeline_renderComicPage and pipeline_refineComicPageRender tools (COST_RENDER, longRunning), with option shapes mirroring the route Zod schemas. Registered automatically via PIPELINE_TOOLS.

The persist runs inside the write tail's compute step, so a concurrent page edit or sibling render can't be reverted by a stale snapshot. The filename hook only attaches the completed image when the slot already carries the returned jobId — the reason this write must live behind the shared entry points, not only in the route.

Test plan

  • server/services/pipeline/visualStages.test.js — new renderComicPage (enqueue + persist) block (proof slot, final+fromProof, 404); refine happy-path now asserts the persisted slot.
  • server/services/creative/toolRegistry.test.js — new dispatch tests for both tools (charges one render action; Zod gate rejects missing pageIndex / instruction).
  • server/routes/pipeline.test.js — mock rewired to drive the real write tail through the new service entry points; page-render/refine-render persistence assertions preserved.
  • Full affected suites green: 446 passed (visualStages, toolRegistry, routes/pipeline, seriesAutopilot).

Refs #2220. Context: docs/plans/2026-07-04-creative-director-orchestrator.md, epic #2182.

@atomantic

Copy link
Copy Markdown
Owner Author

Review gate (claude + codex): no blocking issues. Two non-blocking consistency notes, deferred as out-of-scope for this mirror PR:

  • Deferred (consistency): the CDO page-render tool width/height use z.number().int().positive(), looser than the route's imageEdgeSchema + refineImagePixelCap pixel cap. This is intentionally consistent with the already-shipped coverRenderOptionsShape (CDO creative render tools with route-level cover persistence #2234) — the CDO render tools share one loose numeric shape. Tightening only the page tools would make them stricter than their sibling cover tools; the right fix is to tighten all CDO render-tool schemas uniformly (importing the route's imageEdgeSchema/pixel-cap into creative/tools/pipeline.js), which reaches into CDO creative render tools with route-level cover persistence #2234's shipped covers and belongs in a dedicated follow-up. Enqueue itself still clamps downstream; worst case is a tool call the route would 400.
  • Deferred (cosmetic): referencePage is advertised as { type: 'string' } in the tool's OpenAI parameters while its Zod schema accepts string|int; the parameter description already documents the numeric-index option and Zod is the real gate.

@atomantic atomantic merged commit b9d6bb8 into main Jul 7, 2026
2 checks passed
@atomantic atomantic deleted the claim/issue-2241 branch July 7, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CDO: wrap comic page-render + refine-render route handlers as creative tools (route-level persistence)

1 participant